package com.powernode.service.impl;

import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.powernode.constant.AuthConstants;
import com.powernode.constant.ManagerConstants;
import com.powernode.domain.SysMenu;
import com.powernode.exception.handler.BusinessException;
import com.powernode.mapper.SysMenuMapper;
import com.powernode.model.Result;
import com.powernode.model.SecurityUser;
import com.powernode.service.SysMenuService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.CacheConfig;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.servlet.http.HttpServletRequest;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;

@Service
@Slf4j
// 配置key的前缀
@CacheConfig(cacheNames = "com.powernode.service.impl.SysMenuServiceImpl")
public class SysMenuServiceImpl extends ServiceImpl<SysMenuMapper, SysMenu> implements SysMenuService{


    @Autowired
    private StringRedisTemplate redisTemplate;

    @Autowired
    private SysMenuMapper sysMenuMapper;
    /**
     * 弃用:通过用户id查询用户所有的菜单权限和操作权限
     *
     * @param request
     * @return
     */
    @Override
    @Deprecated
    public Result<Set<String>>  getNavById(HttpServletRequest request) {
        // 通过请求头拿到用户的token
        String authorization = request.getHeader(AuthConstants.AUTHORIZATION);

        String token = authorization.replace(AuthConstants.BEARER, "");

        log.info("系统权限-->用户token:{}",token);

        // 从redis中获取用户token

        String redisKey = AuthConstants.LOGIN_TOKEN_PREFIX + token;

        String redisValue = redisTemplate.opsForValue().get(redisKey);

        // 将redisValue反序列化为User对象
        SecurityUser securityUser = JSON.parseObject(redisValue, SecurityUser.class);

        // 拿到用户的id,查询用户所拥有的所有菜单权限和操作权限
        Long userId = securityUser.getUserId();

        Set<String> navList =  sysMenuMapper.selectNavById(userId);

        Result<Set<String>> success = Result.success(navList);

        return success;
    }


    /**
     * 老师:通过用户id查询用户所有的菜单权限
     *
     *  注解式缓存使用步骤
     *      第一步:在启动类上开启注解式缓存 @EnableCaching
     *      第二步:在服务类上使用@CacheConfig(cacheNames="全限定类名")
     *      第三步:在方法上指定key
     * @param loginUserId
     * @return
     */
    @Cacheable(key = "#loginUserId")
    @Override
    public Set<SysMenu> getUserMenuListByUserId(Long loginUserId) {
        Set<SysMenu> sysMenus = sysMenuMapper.selectMenusByUserId(loginUserId);

        // 将菜单权限转换成树结构
        Set<SysMenu> sysMenusTree = transFromTree(sysMenus,0L);

        return sysMenusTree;
    }

    /**
     * 查询所有菜单信息
     * 将菜单信息保存进redis中
     * @return
     */
    @Override
    @Cacheable(key = ManagerConstants.SYS_ALL_MENU_LIST_KEY)
    public List<SysMenu> queryMenuList() {
        return sysMenuMapper.selectList(null);
    }

    /**
     * 保存菜单信息
     *
     * @param sysMenu
     * @return
     */
    @Override
    @CacheEvict(key = ManagerConstants.SYS_ALL_MENU_LIST_KEY)
    public Integer saveMenu(SysMenu sysMenu) {
        int insert = sysMenuMapper.insert(sysMenu);
        return insert;
    }

    /**
     * 根据id查询菜单信息
     *
     * @param menuId
     */
    @Override
    public SysMenu queryMenuById(Long menuId) {
        return sysMenuMapper.selectById(menuId);
    }

    /**
     * 修改菜单信息
     *
     * @param sysMenu
     * @return
     */
    @Override
    @CacheEvict(key = ManagerConstants.SYS_ALL_MENU_LIST_KEY)
    public Integer updateMenuById(SysMenu sysMenu) {

        int i = sysMenuMapper.updateById(sysMenu);
        return i;
    }

    /**
     * 删除菜单信息
     *
     * @param menuId
     * @return
     */
    @Override
    @CacheEvict(key = ManagerConstants.SYS_ALL_MENU_LIST_KEY)
    @Transactional(rollbackFor = Exception.class)
    public Integer deleteMenuById(Long menuId) {
        int i = 0;
        // 如果菜单对象的类型是0或者1的话,才会有子,判断如果有子则不删除,提示需要先删除子
        SysMenu sysMenu = sysMenuMapper.selectById(menuId);

        if (sysMenu.getType() == 1 || sysMenu.getType() == 0){
            // 再判断下面有没有子
            List<SysMenu> sysMenus = sysMenuMapper.selectList(new LambdaQueryWrapper<SysMenu>().eq(SysMenu::getParentId, menuId));
            if (sysMenus!= null && sysMenus.size() > 0){
                throw new BusinessException("请先删除子菜单");
            }
            // 如果没有子,则直接删除该菜单或者目录
            i = sysMenuMapper.deleteById(menuId);
        }
        // 如果菜单对象的类型是2的话,则没有子,为按钮,直接删除


        if (sysMenu.getType() == 2){
             i = sysMenuMapper.deleteById(menuId);
        }

        return i;
    }

    /**
     * 将set集合里面的数据转换成树结构
     *  1. 已知节点深度写法
     *
     *  2. 未知节点深度写法
     * @param sysMenus
     * @param pid
     * @return
     */
    private Set<SysMenu> transFromTree(Set<SysMenu> sysMenus, long pid) {

        // 1. 下面是已知节点深度写法
        // 拿到root根 [一级菜单]
        //Set<SysMenu> roots = sysMenus.stream().filter(m -> m.getParentId().equals(pid)).collect(Collectors.toSet());
        //
        //roots.forEach(root ->{
        //    Set<SysMenu> child = sysMenus.stream()
        //            .filter(m -> m.getParentId().equals(root.getMenuId()))
        //            .collect(Collectors.toSet());
        //    root.setList(child);
        //});

        // 2. 下面是未知节点深度写法
        // 拿到root根 [一级菜单]
        Set<SysMenu> roots = sysMenus.stream().filter(m -> m.getParentId().equals(pid)).collect(Collectors.toSet());

        // 循环节点集合
        // 使用递归
        roots.forEach( r -> r.setList(transFromTree(sysMenus,r.getMenuId())));

        return roots;
    }
}
